
import model.EquipmentModel;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

public class ServerAcceptor extends Thread {
    private final AcceptListener listener;
    private final Selector selector;
    private HashMap<String, ConnectorHandler> socketChannelMap;
    private final AtomicBoolean listLock = new AtomicBoolean(false);
    private final SocketCloseClass socketCloseClass;

    public SocketCloseClass getSocketCloseClass() {
        return socketCloseClass;
    }

    private class SocketCloseClass implements SocketCloseInterface {
        @Override
        public void SocketConnectorClose(String ipAndPort) {
            listener.SocketConnectorClose(ipAndPort);
        }

        @Override
        public void onSocketMessageArrived(ConnectorHandler connectorHandler, ByteBuffer byteBuffer) {
            listener.onSocketMessageArrived(connectorHandler, byteBuffer);
        }
    }

    public HashMap<String, ConnectorHandler> getSocketChannelMap() {
        return socketChannelMap;
    }

    /**
     * CountDownLatch能够使一个或多个线程等待其他线程完成各自的工作后再执行
     */
    private final CountDownLatch latch = new CountDownLatch(1);
    private boolean done = false;

    ServerAcceptor(AcceptListener listener) throws IOException {
        super("Server-Accept-Thread");
        this.listener = listener;
        this.selector = Selector.open();
//        this.socketChannelList = new ArrayList<>();
        this.socketChannelMap = new HashMap<>();
        socketCloseClass = new SocketCloseClass();
    }

    public synchronized int getSocketListCount() {
        if (socketChannelMap != null) {
            return this.socketChannelMap.size();
        } else {
            return 0;
        }
    }

    public synchronized List<ConnectorHandler> getDisplayChannel() {
        return getDisplayChannel((byte) 0);
    }

    public synchronized List<ConnectorHandler> getDisplayChannel(byte clientType) {
        if (this.socketChannelMap == null) {
            return null;
        }
        if (!this.socketChannelMap.isEmpty()) {
            final Set<String> strings = this.socketChannelMap.keySet();
            List<ConnectorHandler> socketChannels = new ArrayList<>();
            for (String str : strings) {
                final ConnectorHandler connectorHandler = this.socketChannelMap.get(str);
                if (connectorHandler.getClientType() > clientType)
                    socketChannels.add(connectorHandler);
            }
            return socketChannels.isEmpty() ? null : socketChannels;
        } else
            return null;
    }

    public void removeSocketList(String ipAndPort) {
        if (listLock.compareAndSet(false, true)) {
            socketChannelMap.remove(ipAndPort);
            listLock.set(false);
        }
    }

    public void addSocketList(ConnectorHandler connectorHandler) {
        if (listLock.compareAndSet(false, true)) {
            try {
                this.socketChannelMap.put(connectorHandler.getSocketChannel().getRemoteAddress().toString(), connectorHandler);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                listLock.set(false);
            }
        } else {
            try {
                listLock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    boolean awaitRunning() {
        try {
            latch.await();//调用此方法会一直阻塞当前线程，直到计时器的值为0，除非线程被中断。
            return true;
        } catch (InterruptedException e) {
            return false;
        }
    }

    @Override
    public void run() {
        super.run();

        // 回调已进入运行
        latch.countDown();

        Selector selector = this.selector;
        do {
            try {
                if (selector.select() == 0) {
                    if (done) {
                        break;
                    }
                    continue;
                }

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    if (done) {
                        break;
                    }

                    SelectionKey key = iterator.next();
                    iterator.remove();

                    // 检查当前Key的状态是否是我们关注的
                    // 客户端到达状态
                    if (key.isAcceptable()) {
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                        // 非阻塞状态拿到客户端连接
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        ConnectorHandler connectorHandler = new ConnectorHandler();
                        connectorHandler.setUpSocket(socketChannel);
                        String replace = socketChannel.getRemoteAddress().toString().replace("/", "");
                        final String[] split = replace.split(":");
                        if (split != null && split.length > 1) {
                            connectorHandler.setIp(split[0]);
                            connectorHandler.setPort(Integer.valueOf(split[1]));
                            connectorHandler.setClientAddress(socketChannel.getRemoteAddress().toString());
//                            final EquipmentModel equipmentByIp = IoContext.getEquipmentByIp(connectorHandler.getIp(), connectorHandler.getPort());
                            connectorHandler.setCommandAddress(11);
//                            if (equipmentByIp == null) {
//                                connectorHandler.setClientType((byte) 1);
//                            } else {
//                                connectorHandler.setCommandAddress(equipmentByIp.getAddress());
//                            }
                            this.addSocketList(connectorHandler);
                            listener.onNewSocketArrived(connectorHandler);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        } while (!done);

        System.out.println("ServerAcceptor 关闭!");
    }

    void exit() {
        done = true;
        if (selector.isOpen())
            CloseUtils.close(selector);
    }

    Selector getSelector() {
        return selector;
    }

    interface AcceptListener {
        void onNewSocketArrived(ConnectorHandler connectorHandler) throws IOException;

        void SocketConnectorClose(String ipAndPort);

        void onSocketMessageArrived(ConnectorHandler connectorHandler, ByteBuffer byteBuffer);
    }
}